/*
    Spherical Grid Volume Approximation: An algorithm for approximating the 
    volume of a compound object by keeping track of its members' positions in space.
    Copyright (C) 2009, Rodrigo Castano, FuDePAN

    This file is part of Spherical Grid Volume Approximation.

    Spherical Grid Volume Approximation is free software: you can redistribute 
    it and/or modify it under the terms of the GNU General Public License as published 
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Spherical Grid Volume Approximation is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <algorithm>



typedef unsigned int GridCoord;

template <class T>
class Grillado;

// Devuelve "a (mod m)". El operador % devuelve numeros negativos en algunos casos.
// Returns "a (mod m)". The % operator returns negative values in some cases.
inline GridCoord modulo(int a, int m) {
	a = (a%m);
	if(a<0) {
		a += m;
	}
	return static_cast<unsigned int>(a);
}

template <int N>
struct NUM {
	enum {RES = N};
};



struct NIL {};

/*
template <bool condicion, class THEN, class ELSE>
struct IF_CLASS;


template <class THEN, class ELSE>
struct IF_CLASS<true, THEN, ELSE> {
	typedef THEN RES;
};

template < class THEN, class ELSE>
struct IF_CLASS<false, THEN, ELSE> {
	typedef ELSE RES;
};


template <bool condicion, int THEN, int ELSE>
struct IF_ENUM;

template < int THEN, int ELSE>
struct IF_ENUM<true, THEN, ELSE> {
	enum { RES = THEN };
};

template < int THEN, int ELSE>
struct IF_ENUM<false, THEN, ELSE> {
	enum { RES = ELSE };
};

template<class A, class M>
struct MODULO {
	typedef typename IF_CLASS< (static_cast<int>(A::RES) >= static_cast<int>(M::RES)) , 
					MODULO< NUM<A::RES-M::RES> , M > ,  
					typename IF_CLASS< (A::RES < 0) , 
						 MODULO< NUM<A::RES+M::RES> , M > , 
						A 
					>::RES   
				>::RES RES_CLASS;
	enum { RES =  RES_CLASS::RES };
};

template <class COORD, size_t COORD_MAX>
struct COORD_PLUS {
	enum {RES = MODULO< NUM<(static_cast<int>(COORD::RES) + 1)> , NUM<COORD_MAX> >::RES };
};

template <class COORD, size_t COORD_MAX>
struct COORD_MINUS {
	enum {RES = MODULO< NUM<(static_cast<int>(COORD::RES) - 1)> , NUM<COORD_MAX> >::RES };
};






template <class X,  size_t X_MAX >
struct COORD_CALC {
	typedef typename IF_CLASS< (X::RES > 0), 
				COORD_PLUS< 
					COORD_CALC< NUM<X::RES-1> , X_MAX > , X_MAX > , 
				typename IF_CLASS< (X::RES < 0),
					COORD_MINUS< 
						COORD_CALC< NUM<X::RES+1>, X_MAX >  , 
						X_MAX > , 
					NUM<0> 
				>::RES
			>::RES RES_CLASS;
	enum { 	RES =  RES_CLASS::RES };
};
*/
template <int X, int Y, int Z, size_t X_MAX, size_t Y_MAX, size_t Z_MAX >
struct generic_step {
	
	 template<class T_MATRIZ>
	inline static unsigned int apply( T_MATRIZ *** matriz, GridCoord coord_x, GridCoord coord_y, GridCoord coord_z) {
		const GridCoord x = modulo((static_cast<int>(coord_x ) + X) , X_MAX);
		const GridCoord y = modulo((static_cast<int>(coord_y ) + Y) , Y_MAX);
		const GridCoord z = modulo((static_cast<int>(coord_z ) + Z) , Z_MAX);
		return !Grillado<T_MATRIZ>::is_empty_sphere(matriz[x][y][z]);
	};
	 
};

template <int X, int Y, int Z, size_t X_MAX, size_t Y_MAX, size_t Z_MAX >
struct mirror_generic_step  {
	template<class T_MATRIZ>
	inline static unsigned int apply( T_MATRIZ *** matriz, GridCoord coord_x, GridCoord coord_y, GridCoord coord_z) {
		return generic_step<X, Y, Z, X_MAX, Y_MAX, Z_MAX>::apply(matriz, coord_x,coord_y, coord_z) +
		generic_step<-X, -Y, -Z, X_MAX, Y_MAX, Z_MAX>::apply(matriz, coord_x,coord_y, coord_z);
	};
};


// UTIL's only purpose is to avoid " error: function template partial specialization ‘algorithm_list<NIL, T_MATRIZ>’ is not allowed" error
template<class T>
struct UTIL;


template<class T> 
struct UTIL {
	template < class T_MATRIZ >
	inline static unsigned int algorithm_list( T_MATRIZ *** matriz, GridCoord coord_x, GridCoord coord_y, GridCoord coord_z) {
	return T::HEAD::apply(matriz, coord_x, coord_y, coord_z) + UTIL<typename T::TAIL>::algorithm_list(matriz, coord_x, coord_y, coord_z);
	};
};
template <> 
struct UTIL<NIL> {
	template < class T_MATRIZ >
	inline static unsigned int algorithm_list( T_MATRIZ *** matriz, GridCoord coord_x, GridCoord coord_y, GridCoord coord_z) {
	return 0;
	};
};

template <class N, class A>
struct List {
	typedef N HEAD;
	typedef A TAIL;
};

template <class A, class B>
struct CONCAT_OUTOFORDER {
	typedef typename CONCAT_OUTOFORDER< 
		List< typename B::HEAD, A > ,  
		typename B::TAIL 
	>::RES  RES;
};

template <class A>
struct CONCAT_OUTOFORDER<A,NIL> {
	typedef A RES;
};


template <int N, size_t X_MAX, size_t Y_MAX, size_t Z_MAX >
struct Cross {
	typedef typename CONCAT_OUTOFORDER< 
	List< mirror_generic_step<N,0,0, X_MAX, Y_MAX, Z_MAX> , 
		List< mirror_generic_step<0,N,0, X_MAX, Y_MAX, Z_MAX> , 
			List< mirror_generic_step<0,0,N, X_MAX, Y_MAX, Z_MAX> , 
				NIL
			>
		>	
	> , typename Cross<N-1, X_MAX, Y_MAX, Z_MAX >::RES >::RES RES;
	
		
};



template <  size_t X_MAX, size_t Y_MAX, size_t Z_MAX >
struct Cross<0, X_MAX, Y_MAX, Z_MAX > {
	typedef NIL RES;
};

